/**
 * The implementation of LCD1602.
 *
 * @author Jiashu Lin
 *
 * @date   Nov 30, 2011
 */

module Lcd1602P @safe() {
  provides {
    interface Init;
    interface Lcd1602;
  }
  uses {
    interface GeneralIO as RS;
    interface GeneralIO as RW;
    interface GeneralIO as E;
    interface GeneralIO as D0;
    interface GeneralIO as D1;
    interface GeneralIO as D2;
    interface GeneralIO as D3;
    interface GeneralIO as D4;
    interface GeneralIO as D5;
    interface GeneralIO as D6;
    interface GeneralIO as D7;
    interface BusyWait<T32khz,uint16_t> as Delay;

    }
}

implementation {

	void _nop()
	{
		call Delay.wait(30);
	}


  void WaitForEnable() {
	call D0.makeInput();
	call D1.makeInput();
	call D2.makeInput();
	call D3.makeInput();
	call D4.makeInput();
	call D5.makeInput();
	call D6.makeInput();
	call D7.makeInput();
	call RS.clr();
	call RW.set();
	_nop();
	call E.set();
	_nop();
	_nop();

	while(call D7.get());
	
	call E.clr();

	call D0.makeOutput();
	call D1.makeOutput();
	call D2.makeOutput();
	call D3.makeOutput();
	call D4.makeOutput();
	call D5.makeOutput();
	call D6.makeOutput();
	call D7.makeOutput();
	
  }



  /**
   * Screen clear, set AC to 0 cursor position
   */
  async command void Lcd1602.ScreenClear() {
	call RS.clr();
	call RW.clr();
	call D7.clr();
	call D6.clr();
	call D5.clr();
	call D4.clr();
	call D3.clr();
	call D2.clr();
	call D1.clr();
	call D0.set();

	call E.set();
	_nop();
	call E.clr();

  }

  /**
   * Cursor return, DDRAM AD=0, Return, Content Changeless
   */
  async command void Lcd1602.CursorReturn() {
	call RS.clr();
	call RW.clr();
	call D7.clr();
	call D6.clr();
	call D5.clr();
	call D4.clr();
	call D3.clr();
	call D2.clr();
	call D1.set();
	call D0.clr();

	call E.set();
	_nop();
	call E.clr();

  }
  
  /**
   * Input Set, set moving direction of cursor, Appoint if move
   * I_D=1: increment Mode; I_D=0: decrement mode
   * S=1: Shift
   */
  async command void Lcd1602.InputSet(uint8_t I_D, uint8_t S) {
	call RS.clr();
	call RW.clr();
	call D7.clr();
	call D6.clr();
	call D5.clr();
	call D4.clr();
	call D3.clr();
	call D2.set();

	if(I_D & 0x1) {
		call D1.set();
	} else {
		call D1.clr();
	}
	
	if(S & 0x1) {
		call D0.set();
	} else {
		call D0.clr();
	}

	call E.set();
	_nop();
	call E.clr();

  }

  /**
   * Display switch, set display on/off, cursor on/off, blink on/off
   *
   */
  async command void Lcd1602.DisplaySwitch(uint8_t D, uint8_t C, uint8_t B) {
	call RS.clr();
	call RW.clr();
	call D7.clr();
	call D6.clr();
	call D5.clr();
	call D4.clr();
	call D3.set();

	if(D & 0x1) {
		call D2.set();
	} else {
		call D2.clr();
	}

	if(C & 0x1) {
		call D1.set();
	} else {
		call D1.clr();
	}
	
	if(B & 0x1) {
		call D0.set();
	} else {
		call D0.clr();
	}

	call E.set();
	_nop();
	call E.clr();

  }

  /**
   * Shift, remove cursor and whole display, DDRAM changeless
   * S_C=1: display shift; S_C=0: cursor shift
   * R_L=1: right shift; R_L=0: left shift
   */
  async command void Lcd1602.Shift(uint8_t S_C, uint8_t R_L) {
	call RS.clr();
	call RW.clr();
	call D7.clr();
	call D6.clr();
	call D5.clr();
	call D4.clr();

	if(S_C & 0x1) {
		call D3.set();
	} else {
		call D3.clr();
	}

	if(R_L & 0x1) {
		call D2.set();
	} else {
		call D2.clr();
	}

	call D1.clr();
	call D0.clr();

	call E.set();
	_nop();
	call E.clr();

  }

   /**
   * Function set, set DL, display line, font
   * DL=1: 8D; DL=0:4D
   * N=1: 2R; N=0: 1R
   * F=1: 5x10 Style; F=0:5x7 Style
   */
  async command void Lcd1602.FunctionSet(uint8_t DL, uint8_t N, uint8_t F) {
	call RS.clr();
	call RW.clr();
	call D7.clr();
	call D6.clr();
	call D5.set();

	if(DL & 0x1) {
		call D4.set();
	} else {
		call D4.clr();
	}

	if(N & 0x1) {
		call D3.set();
	} else {
		call D3.clr();
	}

	if(F & 0x1) {
		call D2.set();
	} else {
		call D2.clr();
	}

	call D1.clr();
	call D0.clr();

	call E.set();
	_nop();
	call E.clr();
	
  }

 
  /**
   * CGRAM AD set, set CGRAM AD, send receive data
   * ACG: CGRAM AD
   */
  async command void Lcd1602.CgramAdSet(uint8_t ACG) {
	call RS.clr();
	call RW.clr();
	call D7.clr();
	call D6.set();

	if(ACG & (0x1 << 5)) {
		call D5.set();
	} else {
		call D5.clr();
	}

	if(ACG & (0x1 << 4)) {
		call D4.set();
	} else {
		call D4.clr();
	}

	if(ACG & (0x1 << 3)) {
		call D3.set();
	} else {
		call D3.clr();
	}

	if(ACG & (0x1 << 2)) {
		call D2.set();
	} else {
		call D2.clr();
	}

	if(ACG & (0x1 << 1)) {
		call D1.set();
	} else {
		call D1.clr();
	}

	if(ACG & (0x1 << 0)) {
		call D0.set();
	} else {
		call D0.clr();
	}

	call E.set();
	_nop();
	call E.clr();

  }

  /**
   * DDRAM AD set, set DDRAM AD, send receive data
   * ADD: DDRAM AD & Cursor AD & Cursor AD
   */
  async command void Lcd1602.DdramAdSet(uint8_t ADD) {
	call RS.clr();
	call RW.clr();
	call D7.set();

	if(ADD & (0x1 << 6)) {
		call D6.set();
	} else {
		call D6.clr();
	}

	if(ADD & (0x1 << 5)) {
		call D5.set();
	} else {
		call D5.clr();
	}

	if(ADD & (0x1 << 4)) {
		call D4.set();
	} else {
		call D4.clr();
	}

	if(ADD & (0x1 << 3)) {
		call D3.set();
	} else {
		call D3.clr();
	}

	if(ADD & (0x1 << 2)) {
		call D2.set();
	} else {
		call D2.clr();
	}

	if(ADD & (0x1 << 1)) {
		call D1.set();
	} else {
		call D1.clr();
	}

	if(ADD & (0x1 << 0)) {
		call D0.set();
	} else {
		call D0.clr();
	}

	call E.set();
	_nop();
	call E.clr();

  }

   /**
   * Busy/ad read CT, Executing internal function, reading AD of CT
   * BF=1: execute internal function; BF=0: command received
   */
  async command void Lcd1602.BusyAdReadCt(uint8_t BF, uint8_t AC) {
	call RS.clr();
	call RW.set();

	if(BF & 0x1) {
		call D7.set();
	} else {
		call D7.clr();
	}

	if(AC & (0x1 << 6)) {
		call D6.set();
	} else {
		call D6.clr();
	}

	if(AC & (0x1 << 5)) {
		call D5.set();
	} else {
		call D5.clr();
	}

	if(AC & (0x1 << 4)) {
		call D4.set();
	} else {
		call D4.clr();
	}

	if(AC & (0x1 << 3)) {
		call D3.set();
	} else {
		call D3.clr();
	}

	if(AC & (0x1 << 2)) {
		call D2.set();
	} else {
		call D2.clr();
	}

	if(AC & (0x1 << 1)) {
		call D1.set();
	} else {
		call D1.clr();
	}

	if(AC & (0x1 << 0)) {
		call D0.set();
	} else {
		call D0.clr();
	}

	call E.set();
	_nop();
	call E.clr();

  }

  /**
   * CGRAM/DDRAM data write, write data from CFGRAM or DDRAM
   * DATA: data to write
   */
  async command void Lcd1602.CgramDdramDataWrite(uint8_t DATA) {
	WaitForEnable();

	call RS.set();
	call RW.clr();

	if(DATA & (0x1 << 7)) {
		call D7.set();
	} else {
		call D7.clr();
	}

	if(DATA & (0x1 << 6)) {
		call D6.set();
	} else {
		call D6.clr();
	}

	if(DATA & (0x1 << 5)) {
		call D5.set();
	} else {
		call D5.clr();
	}

	if(DATA & (0x1 << 4)) {
		call D4.set();
	} else {
		call D4.clr();
	}

	if(DATA & (0x1 << 3)) {
		call D3.set();
	} else {
		call D3.clr();
	}

	if(DATA & (0x1 << 2)) {
		call D2.set();
	} else {
		call D2.clr();
	}

	if(DATA & (0x1 << 1)) {
		call D1.set();
	} else {
		call D1.clr();
	}

	if(DATA & (0x1 << 0)) {
		call D0.set();
	} else {
		call D0.clr();
	}

	call E.set();
	_nop();
	call E.clr();

  }

  /**
   * CGRAM/DDRAM data read, read data from CFGRAM or DDRAM
   * DATA: data to read
   */
  async command void Lcd1602.CgramDdramDataRead(uint8_t DATA) {
	call RS.set();
	call RW.set();

	if(DATA & (0x1 << 7)) {
		call D7.set();
	} else {
		call D7.clr();
	}

	if(DATA & (0x1 << 6)) {
		call D6.set();
	} else {
		call D6.clr();
	}

	if(DATA & (0x1 << 5)) {
		call D5.set();
	} else {
		call D5.clr();
	}

	if(DATA & (0x1 << 4)) {
		call D4.set();
	} else {
		call D4.clr();
	}

	if(DATA & (0x1 << 3)) {
		call D3.set();
	} else {
		call D3.clr();
	}

	if(DATA & (0x1 << 2)) {
		call D2.set();
	} else {
		call D2.clr();
	}

	if(DATA & (0x1 << 1)) {
		call D1.set();
	} else {
		call D1.clr();
	}

	if(DATA & (0x1 << 0)) {
		call D0.set();
	} else {
		call D0.clr();
	}

	call E.set();
	_nop();
	call E.clr();

  }

    command error_t Init.init() {
	call RS.makeOutput();
	call RW.makeOutput();
	call E.makeOutput();
	call D0.makeOutput();
	call D1.makeOutput();
	call D2.makeOutput();
	call D3.makeOutput();
	call D4.makeOutput();
	call D5.makeOutput();
	call D6.makeOutput();
	call D7.makeOutput();

	call Lcd1602.FunctionSet(1, 1, 0);
	call Delay.wait(150);
	call Lcd1602.FunctionSet(1, 1, 0);
	call Delay.wait(150);
	call Lcd1602.FunctionSet(1, 1, 0);
	call Delay.wait(150);
	WaitForEnable();
	call Lcd1602.FunctionSet(1, 1, 0);
	WaitForEnable();
	call Lcd1602.DisplaySwitch(0, 0, 0);
	WaitForEnable();
	call Lcd1602.ScreenClear();
	WaitForEnable();
	call Lcd1602.InputSet(1, 0);
	WaitForEnable();
	call Lcd1602.DisplaySwitch(1, 1, 1);

	return SUCCESS;
	}

}
